//shp转换
var toGeoJSON = (function() {
	var removeSpace = /\s*/g,
        trimSpace = /^\s*|\s*$/g,
        splitSpace = /\s+/;
	//生成字符串的短、数字散列
	function okhash(x){
		if(!x || !x.length) return 0;
		for (var i = 0, h = 0; i < x.length; i++) {
			h = ((h << 5) - h) + x.charCodeAt(i) | 0; //charCodeAt() 方法可返回指定位置的字符的 Unicode 编码。这个返回值是 0 - 65535 之间的整数。
		}
		return h;
	}
	function get(x,y){return x.getElementsByTagName(y);}
	function get1(x,y){var n = get(x,y); return n.length ? n[0] : null;} //获取element该标签下面的数组
	function attr(x,y){return x.getAttribute(y);}
	function nodeVal (x) {
		if(x){norm(x);}
		return (x && x.textContent) || '';
	}
	function coord1(v){return numarray(v.replace(removeSpace, '').split(','));}
	function coord(v){
		var coords = v.replace(trimSpace, '').split(splitSpace),
			o = [];
		for(var i =0; i < coords.length; i++) {
			o.push(coord1(coords[i]));
		}
		return o;
	}
	// https://developer.mozilla.org/en-US/docs/Web/API/Node.normalize
	function norm(el){ if (el.normalize) { el.normalize(); }  return el; } //normalize() 方法移除空的文本节点，并连接相邻的文本节点。
	function numarray(x){
		for(var j = 0, o = []; j < x.length; j++) {o[j] = parseFloat(x[j]);}
		return o;
	}
	function fc() {
		return {
			type: 'FeatureCollection',
			features:[]
		}
	}
	var serializer;

	if (typeof XMLSerializer !== 'undefined') {
		serializer = new XMLSerializer();//XMLSerializer 对象使你能够把一个 XML 文档或 Node 对象转化或“序列化”为未解析的 XML 标记的一个字符串
	} else if(typeof exports === 'object' && typeof process === 'object' && !process.browser) {
		// serializer = new (require('xmldom').XMLSerializer)();
	}
	function xml2str(str){
		if(str.xml !== undefined) return str.xml;
		return serializer.serializeToString(str);
	}
	var t = {
		kml: function(doc){
			var gj = fc(),
				styleIndex = {}, styleByHash = {},
				styleMapIndex = {},
				geotypes = ['Polygon', 'LineString', 'Point', 'Track', 'gx:Track'], //LinearRing圆环 MultiGeometry复合对象元素
				placemarks = get(doc, 'Placemark'),
				styles = get(doc, 'Style'),
				styleMaps = get(doc, 'StyleMap');
			//<Style>
			for(var k = 0; k < styles.length; k++) {
				var hash = okhash(xml2str(styles[k])).toString(16);
				styleIndex['#' + attr(styles[k], 'id')] = hash;
				styleByHash[hash] = styles[k];
			}
			//<StyleMap>
			for (var l = 0; l < styleMaps.length; l++) {
				styleIndex['#' + attr(styleMaps[l], 'id')] = okhash(xml2str(styleMaps[l])).toString(16);//StyleMap高亮显示
				var pairs = get(styleMaps[l], 'Pair');//单一标签，是<Pair>的子元素.用来定义一个高亮或正常情况下的样式
				var pairsMap = {};
				for (var m = 0; m < pairs.length; m++){
					pairsMap[nodeVal(get1(pairs[m], 'key'))] = nodeVal(get1(pairs[m], 'styleUrl'));
				}
				styleMapIndex['#' + attr(styleMaps[l], 'id')] = pairsMap;
			}
			//<Placemark>
			for (var j = 0; j < placemarks.length; j++) {
				gj.features = gj.features.concat(getPlacemark(placemarks[j]));
			}
			function kmlColor(v){
				var color, opacity;
                v = v || '';
                if (v.substr(0, 1) === '#') { v = v.substr(1); }
                if (v.length === 6 || v.length === 3) { color = v; }
                if (v.length === 8) {
                    opacity = parseInt(v.substr(0, 2), 16) / 255;
                    color = '#' + v.substr(6, 2) +
                        v.substr(4, 2) +
                        v.substr(2, 2);
                }
                return [color, isNaN(opacity) ? undefined : opacity];
			}
			function gxCoord(v){return numarray(v.split(' '));}
			function gxCoords(root){
				var elems = get(root, 'coord', 'gx'), coords = [], times = [];
				if(elems.length === 0) elems = get(root, 'gx:coord');
				for (var i = 0; i < elems.length; i++) coords.push(gxCoord(nodeVal(elems[i])));
				var timeElems = get(root, 'when');
				for (var j = 0; j < timeElems.length; j++) times.push(nodeVal(timeElems[j]));
				return {
                    coords: coords,
                    times: times
                };
			}
			function getGeometry(root){
				var geomNode, geomNodes, i, j, k, geoms = [], coordTimes = [];
				/*多几何体时*/
				if(get1(root, 'MultiGeometry')) {return getGeometry(get1(root, 'MultiGeometry'));}
				if (get1(root, 'MultiTrack')) { return getGeometry(get1(root, 'MultiTrack')); }
                if (get1(root, 'gx:MultiTrack')) { return getGeometry(get1(root, 'gx:MultiTrack')); }

                for (i = 0; i < geotypes.length; i++) {
                	geomNodes = get(root, geotypes[i]);
                	if(geomNodes){
                		for (j = 0; j < geomNodes.length; j++) {
                			geomNode = geomNodes[j];
                			if(geotypes[i] === 'Point') {
                				geoms.push({
                					type: 'Point',
                					coordinates: coord1(nodeVal(get1(geomNode, 'coordinates')))
                				});
                			}else if(geotypes[i] === 'LineString') {
												let coordinatess = nodeVal(get1(geomNode, 'coordinates'));
                				geoms.push({
                					type: 'LineString',
                					coordinates: coordinatess?coord(coordinatess):[]
                				})
                			} else if(geotypes[i] === 'Polygon'){
                				var rings = get(geomNode, 'LinearRing'),
                                    coords = [];
                                for (k = 0; k < rings.length; k++) {
                                    coords.push(coord(nodeVal(get1(rings[k], 'coordinates'))));
                                }
                                geoms.push({
                                    type: 'Polygon',
                                    coordinates: coords
                                });
                			} else if(geotypes[i] === 'Track' ||
                                geotypes[i] === 'gx:Track') {
                				var track = gxCoords(geomNode);
                				geoms.push({
                                    type: 'LineString',
                                    coordinates: track.coords
                                });
                                if (track.times.length) coordTimes.push(track.times);
                			}
                		}
                	}
                }
                return {
                    geoms: geoms,
                    coordTimes: coordTimes
                };
			}
			function getPlacemark(root){
				var geomsAndTimes = getGeometry(root),i,properties = {},
					name = nodeVal(get1(root, 'name')),
					styleUrl = nodeVal(get1(root, 'styleUrl')),
					description = nodeVal(get1(root, 'description')),
					timeSpan = get1(root, 'TimeSpan'),
					timeStamp = get1(root, 'TimeStamp'),
					extendedData = get1(root, 'ExtendedData'),
					lineStyle = get1(root, 'LineStyle'),
					polyStyle = get1(root, 'PolyStyle'),
					visibility = get1(root, 'visibility');

				if(!geomsAndTimes.geoms.length) return [];
				if(name) properties.name = name;
				if(styleUrl) {
					if (styleUrl[0] !== '#') {
						styleUrl = '#' + styleUrl;
					}
					properties.styleUrl = styleUrl;
					if (styleIndex[styleUrl]) {
						properties.styleHash = styleIndex[styleUrl];
					}
					if (styleMapIndex[styleUrl]) {
						properties.styleMapHash = styleMapIndex[styleUrl];
						properties.styleHash = styleIndex[styleMapIndex[styleUrl].normal];
					}
					var style = styleByHash[properties.styleHash];
					if(style){
						if(!lineStyle) lineStyle = get1(style,'LineStyle');
						if(!polyStyle) polyStyle = get1(style,'PolyStyle');
					}
				}
				if (description) properties.description = description;
				if (timeSpan) {
					var begin = nodeVal(get1(timeSpan, 'begin'));
					var end = nodeVal(get1(timeSpan, 'end'));
					properties.timespan = {begin: begin, end: end};
				}
				if(timeStamp) {
					properties.timestamp = nodeVal(get1(timeStamp, 'when'));
				}
				if(lineStyle){
					var linestyles = kmlColor(nodeVal(get1(lineStyle, 'color'))),
						color = linestyles[0],
						opacity = linestyles[1],
						width = parseFloat(nodeVal(get1(lineStyle, 'width')));
					if (color) {
						properties.color = color;	
					}
					if(!isNaN(opacity)) properties.opacity = opacity;
					if(!isNaN(width)) properties.weight = width;
				}
				if(polyStyle){
					var polystyles = kmlColor(nodeVal(get1(polyStyle, 'color'))),
						pcolor = polystyles[0],
						popacity = polystyles[1],
						fill = nodeVal(get1(polyStyle, 'fill')),
						outline = nodeVal(get1(polyStyle, 'outline'));
					if(pcolor) properties.fillColor = pcolor;
					if(!isNaN(popacity)) properties.fillOpacity = popacity;
					if(fill) properties.fillOpacity = fill === '1' ? properties.fillOpacity || 1 : 0;
					if(outline) properties.opacity = outline === '1' ? properties.opacity || 1 : 0;
				}
				if(extendedData){
					var datas = get(extendedData, 'Data'),
						simpleDatas = get(extendedData, 'SimpleData');
					for(i = 0; i < datas.length; i++){
						properties[datas[i].getAttribute('name')] = nodeVal(get1(datas[i], 'value'));
					}
					for (i = 0; i < simpleDatas.length; i++) {
						properties[simpleDatas[i].getAttribute('name')] = nodeVal(simpleDatas[i]);
					}
				}
				if(visibility){
					properties.visibility = nodeVal(visibility);
				}
				if(geomsAndTimes.coordTimes.length){
					properties.coordTimes = (geomsAndTimes.coordTimes.length === 1)?
						geomsAndTimes.coordTimes[0]: geomsAndTimes.coordTimes;
				}
				var feature = {
					type: 'Feature',
					geometry: (geomsAndTimes.geoms.length === 1) ? geomsAndTimes.geoms[0] : {
						type: 'GeometryCollection',
                    	geometries: geomsAndTimes.geoms
					},
					properties: properties
				};
				if (attr(root, 'id')) feature.id = attr(root, 'id');
				return [feature];
			}
			return gj;
		},
		shp: function (shp$$1,options) {
			var parseNull = function() {
			  return null;
			};
			var parsePoint = function(record) {
			  return {type: "Point", coordinates: [record.getFloat64(4, true), record.getFloat64(12, true)]};
			};

			var parsePolygon = function(record) {
			  var i = 44, j, n = record.getInt32(36, true), m = record.getInt32(40, true), parts = new Array(n), points = new Array(m), polygons = [], holes = [];
			  for (j = 0; j < n; ++j, i += 4) parts[j] = record.getInt32(i, true);
			  for (j = 0; j < m; ++j, i += 16) points[j] = [record.getFloat64(i, true), record.getFloat64(i + 8, true)];
			  parts.forEach(function(i, j) {
			    var ring = points.slice(i, parts[j + 1]);
			    if (ringClockwise(ring)) polygons.push([ring]);
			    else holes.push(ring);
			  });
			  holes.forEach(function(hole) {
			    polygons.some(function(polygon) {
			      if (ringContainsSome(polygon[0], hole)) {
			        polygon.push(hole);
			        return true;
			      }
			    }) || polygons.push([hole]);
			  });
			  return polygons.length === 1
			      ? {type: "Polygon", coordinates: polygons[0]}
			      : {type: "MultiPolygon", coordinates: polygons};
			};
			function ringClockwise(ring) {
			  if ((n = ring.length) < 4) return false;
			  var i = 0, n, area = ring[n - 1][1] * ring[0][0] - ring[n - 1][0] * ring[0][1];
			  while (++i < n) area += ring[i - 1][1] * ring[i][0] - ring[i - 1][0] * ring[i][1];
			  return area >= 0;
			}

			function ringContainsSome(ring, hole) {
			  var i = -1, n = hole.length, c;
			  while (++i < n) {
			    if (c = ringContains(ring, hole[i])) {
			      return c > 0;
			    }
			  }
			  return false;
			}

			function ringContains(ring, point) {
			  var x = point[0], y = point[1], contains = -1;
			  for (var i = 0, n = ring.length, j = n - 1; i < n; j = i++) {
			    var pi = ring[i], xi = pi[0], yi = pi[1],
			        pj = ring[j], xj = pj[0], yj = pj[1];
			    if (segmentContains(pi, pj, point)) {
			      return 0;
			    }
			    if (((yi > y) !== (yj > y)) && ((x < (xj - xi) * (y - yi) / (yj - yi) + xi))) {
			      contains = -contains;
			    }
			  }
			  return contains;
			}

			function segmentContains(p0, p1, p2) {
			  var x20 = p2[0] - p0[0], y20 = p2[1] - p0[1];
			  if (x20 === 0 && y20 === 0) return true;
			  var x10 = p1[0] - p0[0], y10 = p1[1] - p0[1];
			  if (x10 === 0 && y10 === 0) return false;
			  var t = (x20 * x10 + y20 * y10) / (x10 * x10 + y10 * y10);
			  return t < 0 || t > 1 ? false : t === 0 || t === 1 ? true : t * x10 === x20 && t * y10 === y20;
			}
			var parsePolyLine = function(record) {
			  var i = 44, j, n = record.getInt32(36, true), m = record.getInt32(40, true), parts = new Array(n), points = new Array(m);
			  for (j = 0; j < n; ++j, i += 4) parts[j] = record.getInt32(i, true);
			  for (j = 0; j < m; ++j, i += 16) points[j] = [record.getFloat64(i, true), record.getFloat64(i + 8, true)];
			  return n === 1
			      ? {type: "LineString", coordinates: points}
			      : {type: "MultiLineString", coordinates: parts.map(function(i, j) { return points.slice(i, parts[j + 1]); })};
			};
			var parseMultiPoint = function(record) {
			  var i = 40, j, n = record.getInt32(36, true), coordinates = new Array(n);
			  for (j = 0; j < n; ++j, i += 16) coordinates[j] = [record.getFloat64(i, true), record.getFloat64(i + 8, true)];
			  return {type: "MultiPoint", coordinates: coordinates};
			};
			var empty = new Uint8Array(0);
			function concat(a,b){
				if (!a.length) return b;
				if (!b.length) return a;
				var c = new Uint8Array(a.length + b.length);
				c.set(a);
				c.set(b, a.length);
				return c;
			}
			var concat$1 = function(a, b) {
			  var ab = new Uint8Array(a.length + b.length);
			  ab.set(a, 0);
			  ab.set(b, a.length);
			  return ab;
			};
			var view = function(array) {
			  return new DataView(array.buffer, array.byteOffset, array.byteLength);//可使用 DataView 对象在 ArrayBuffer 中的任何位置读取和写入不同类型的二进制数据。
			};
			var array_cancel = function() {
			  	this._array = null;
			  	return Promise.resolve();
			};

			var array_read = function() {
			  	var array = this._array;
			  	this._array = null;
			  	return Promise.resolve(array ? {done: false, value: array} : {done: true, value: undefined});
			};
			function array(array){
				return new ArraySource(array instanceof Uint8Array ? array : new Uint8Array(array));
			}
			function ArraySource(array) {
			  this._array = array;
			}
			ArraySource.prototype.read = array_read;
			ArraySource.prototype.cancel = array_cancel;

			var slice_cancel = function() {
			   	return this._source.cancel();
			};
			var slice_read = function(){
				var that = this,array = that._array.subarray(that._index);
				return that._source.read().then(function(result){
					that._array = empty;
					that._index = 0;
					return result.done ? (array.length > 0
						? {done: false, value: array}
						: {done: true,value: undefined})
					:{done:false, value:concat(array, result.value)};
				})
			}
			var slice_slice = function (length) {
				if ((length |= 0) < 0) throw new Error("invalid length");
				var that = this, index = this._array.length - this._index;
				// If the request fits within the remaining buffer, resolve it immediately.
				if (this._index + length <= this._array.length) {
				    return Promise.resolve(this._array.subarray(this._index, this._index += length));
				}
				// Otherwise, read chunks repeatedly until the request is fulfilled.
			  	var array = new Uint8Array(length);
				array.set(this._array.subarray(this._index));
				return (function read() {
    				return that._source.read().then(function(result) {
					    // When done, it’s possible the request wasn’t fully fullfilled!
					    // If so, the pre-allocated array is too big and needs slicing.
					    if (result.done) {
					        that._array = empty;
					        that._index = 0;
					        return index > 0 ? array.subarray(0, index) : null;
					    }
					    // If this chunk fulfills the request, return the resulting array.
					    if (index + result.value.length >= length) {
					        that._array = result.value;
					        that._index = length - index;
					        array.set(result.value.subarray(0, length - index), index);
					        return array;
					    }
						// Otherwise copy this chunk into the array, then read the next chunk.
						array.set(result.value, index);
						index += result.value.length;
						return read();
					});
				})();
			}
			function slice(source){
				return typeof source.slice === "function" ? source :
			      new SliceSource(typeof source.read === "function" ? source
			          : source.getReader());
			}
			function SliceSource(source) {
			  this._source = source;
			  this._array = empty;
			  this._index = 0;
			}
			SliceSource.prototype.read = slice_read;
			SliceSource.prototype.slice = slice_slice;
			SliceSource.prototype.cancel = slice_cancel;
			var parsers = {
			  0: parseNull,
			  1: parsePoint,
			  3: parsePolyLine,
			  5: parsePolygon,
			  8: parseMultiPoint,
			  11: parsePoint, // PointZ
			  13: parsePolyLine, // PolyLineZ
			  15: parsePolygon, // PolygonZ
			  18: parseMultiPoint, // MultiPointZ
			  21: parsePoint, // PointM
			  23: parsePolyLine, // PolyLineM
			  25: parsePolygon, // PolygonM
			  28: parseMultiPoint // MultiPointM
			};
			function cancel() {
			  return this._source.cancel();
			}
			var shp_read = function() {
			  var that = this;
			  ++that._index;
			  return that._source.slice(12).then(function(array) {
			    if (array == null) return {done: true, value: undefined};
			    var header = view(array);
			    // If the record starts with an invalid shape type (see #36), scan ahead in
			    // four-byte increments to find the next valid record, identified by the
			    // expected index, a non-empty content length and a valid shape type.
			    function skip() {
			      return that._source.slice(4).then(function(chunk) {
			        if (chunk == null) return {done: true, value: undefined};
			        header = view(array = concat$1(array.slice(4), chunk));
			        return header.getInt32(0, false) !== that._index ? skip() : read();
			      });
			    }
			    // All records should have at least four bytes (for the record shape type),
			    // so an invalid content length indicates corruption.
			    function read() {
			      var length = header.getInt32(4, false) * 2 - 4, type = header.getInt32(8, true);
			      return length < 0 || (type && type !== that._type) ? skip() : that._source.slice(length).then(function(chunk) {
			        return {done: false, value: type ? that._parse(view(concat$1(array.slice(8), chunk))) : null};
			      });
			    }
			    return read();
			  });
			};
			var shp = function(source){
				source = slice(source);
				return source.slice(100).then(function(array) {
				    return new Shp(source, view(array));
			  	});
			}
			function Shp(source, header){
				var type = header.getInt32(32, true);
				if(!(type in parsers)) throw new Error("unsupported shape type: " + type);
				this._source = source;
				this._type = type;
				this._index = 0;
				this._parse = parsers[type];
				this.bbox = [header.getFloat64(36,true), header.getFloat64(44,true),header.getFloat64(52,true),header.getFloat64(60, true)]
			}
			var prototype$2 = Shp.prototype;
			prototype$2.read = shp_read;
			prototype$2.cancel = cancel;
			var shapefile_cancel = function() {
			  return Promise.all([
			    this._dbf && this._dbf.cancel(),
			    this._shp.cancel()
			  ]).then(noop);
			};
			var shapefile_read = function() {
			  var that = this;
			  return Promise.all([
			    that._shp.read()
			  ]).then(function(results) {
			    var shp = results[0];
			    return shp.done ? shp : {
			      done: false,
			      value: {
			        type: "Feature",
			        geometry: shp.value
			      }
			    };
			  });
			};
			var shapefile = function(shpSource) {
			  return Promise.all([
			    shp(shpSource)
			  ]).then(function(sources) {
			    return new Shapefile(sources[0]);
			  });
			};
			function Shapefile(shp$$1){
				this._shp = shp$$1;
				this.bbox = shp$$1.bbox;
			}
			var prototype$1 = Shapefile.prototype;
			prototype$1.read = shapefile_read;
			prototype$1.cancel = shapefile_cancel;
			if (shp$$1 instanceof ArrayBuffer || shp$$1 instanceof Uint8Array) {
				shp$$1 = array(shp$$1);
			}
			return Promise.all([shp$$1]).then(function(sources){
				var shp$$1 = sources[0];
				return shapefile(shp$$1);
			})
		}
	}
	return t;
})();
var TransmitFile = {
  
    fileImport:function(files,callback){
        var _this = this;
        if (files.length === 0) { return; }
        var file = files[0];
        if(!file)return;
        var ext = file.name.split('.').pop();
        var reader = new FileReader();

        if(ext.toLowerCase() === 'shp') {
            reader.readAsArrayBuffer(file);
        } else{
            reader.readAsText(file);
        }
        reader.onload = function (e) {
            var content = e.target.result;
            var geojson = null;
            try {
                switch(ext.toLowerCase()){
					case "kml":
						if (typeof content === 'string') {
							content = (new window.DOMParser()).parseFromString(content, 'text/xml');
						}
						geojson = toGeoJSON['kml'](content);
						if(callback)
							callback(geojson);
						break;
                    case "geojson":
                        geojson = JSON.parse(content);
                        if(callback)
                            callback(geojson);
                        break;
                    case "shp":
                        toGeoJSON['shp'](content).then(source => source.read().then(function log(result) {
                            if (result.done) return;
                            if(callback)
                                callback(result.value);
                            return source.read().then(log);
                        })).catch(error => console.error(error.stack));
                        break;
                }
  
            } catch (err) {
                console.log(err)
            }
        }
    }
  }